package com.atzly.community.datasource;

import com.atzly.community.datasource.config.APPEnum;
import com.atzly.community.datasource.service.EnterpriseDbMapService;
import com.atzly.community.datasource.util.ContextHelper;
import com.atzly.community.datasource.util.ServiceContext;
import com.atzly.community.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.TimeUnit;


public class DataSourceConfigManager {
  private static final Logger logger = LoggerFactory.getLogger(DataSourceConfigManager.class);

  public static final Map<String, String> zkProperties = new HashMap<>();
  public static final String CURRENT_DB_ID =  "cc-dbcluster.currentdb";
  public static final String CURRENT_APP_DB_ID = "cc-dbcluster.currentdb.%1$s";
  private static final String DB_ID_LIST = "cc-dbcluster.dblist";
  private static final String APP_DB_ID_LIST = "cc-dbcluster.dblist.%1$s";
  public static final String CLICKHOUSE_DB_ID = "ck";

  public static final String DORIS_DB_ID = "doris";
  public static final String DORIS_DW_DB_ID = "doris_dw";
  public static final String DORIS_HYBIRD_DB_ID = "doris_hybird";

  private static final int MAX_RETRY_TIMES = 10;

  protected static boolean initial = false;

  public static void loadZkProperties() {
    try {
      if (initial) {
        return;
      }
      logger.info("正在初始化DataSourceConfigManager ....");
      Map<String, String> properties = ZookeeperPropertiesLoader.INSTANCE.getProperties();
      zkProperties.putAll(properties);
      logger.info("初始化DataSourceConfigManager完成");
      initial = true;
    } catch (Exception e) {
      logger.warn("error init datasource properties, msg={}", e.getMessage());
    }
  }

  public static String getDbIdByTenantId(Long tenantId) {
    return getDbIdByTenantId(null, tenantId);
  }

  public static String getDbIdByTenantId(Long appId, Long tenantId) {
    return getDbIdByTenantId(appId, tenantId, false);
  }

    /**
     * 通过租户id获取dbid
     * @param appId 慧经盈应用id
     * @param tenantId 租户id
     * @param ignoreContextDb
     * @return
     */
  public static String getDbIdByTenantId(Long appId, Long tenantId, boolean ignoreContextDb) {
    String dbId = ContextHelper.getServiceContext().getShardDbId();
    if (!ignoreContextDb && StringUtil.isNotEmpty(dbId)) {
      return dbId;
    }

    if (appId == null) {
      appId = ContextHelper.getServiceContext().getMainAppId();
    }
    dbId = DataSourceConfigCache.getDbId(appId, tenantId);
    if (StringUtil.isNotEmpty(dbId)) {
      return dbId;
    }

    int times = 0;
    while (true) {
      dbId = _getDbIdByTenantIdId(appId, tenantId);
      if (StringUtil.isNotEmpty(dbId)) {
        break;
      }

      times++;
      if (times == MAX_RETRY_TIMES) {
        logger.error("getDbIdByOrgId: retry {} times", MAX_RETRY_TIMES);
        break;
      }

      try {
        TimeUnit.MILLISECONDS.sleep(30L);
      } catch (InterruptedException e) {
        logger.warn("getDbIdByOrgId: sleep 30 ms", e);
      }
    }

    if (StringUtil.isNotEmpty(dbId)) {
      DataSourceConfigCache.setDbId(appId, tenantId, dbId);
//      JSONObject contextJson = JSONObject.parseObject(JSON.toJSONString(ContextHelper.getServiceContext()));
//      contextJson.put("dbId", dbId);
      ServiceContext serviceContext = ContextHelper.getServiceContext();
      serviceContext.setDbId(dbId);
      ContextHelper.setServiceContext(serviceContext);
    }

    return dbId;
  }

  protected static String _getDbIdByTenantIdId(Long appId, Long tenantId) {
    String dbId = null;
    try {
        if (tenantId != 0) {
          ServiceContext serviceContext = ContextHelper.getServiceContext();
//          serviceContext.setTenantId(0L);
          ContextHelper.setServiceContext(serviceContext);
        }

      EnterpriseDbMapService enterpriseDbMapService = SpringContextUtil.getBean("enterpriseDbMapService");
      String _dbId = getCurrentDbId(appId);
//      dbId = enterpriseDbMapService.queryAndAddIfEmpty(appId, tenantId,_dbId).getData();
//      if (StringUtil.isEmpty(dbId)) {
//        String _dbId = getCurrentDbId(appId);
//        if (StringUtil.isNotEmpty(_dbId)) {
//
//          if (tenantId != 0) {
//            ServiceContext serviceContext = ContextHelper.getServiceContext();
//            serviceContext.setTenantId(0L);
//            ContextHelper.setServiceContext(serviceContext);
//          }
//
//          enterpriseDbMapService.setDbId(appId, tenantId, _dbId).throwIfFailed();
//          dbId = _dbId;
//        }
//      }
    } catch (Exception e) {
      logger.error("queryDbId error, tenantId=" + tenantId, e);
    }finally {
      ServiceContext serviceContext = ContextHelper.getServiceContext();
//      serviceContext.setTenantId(tenantId);
      ContextHelper.setServiceContext(serviceContext);
    }
    return dbId;
  }

  @Deprecated
  public static DataSourceConfig getDataSourceConfigByDbId(String dbId) {
    DataSourceConfig dataSourceConfig = new DataSourceConfig();
    dataSourceConfig.setUrl(zkProperties.get(dbId + ".jdbc.url"));
    dataSourceConfig.setUsername(zkProperties.get(dbId + ".jdbc.username"));
    dataSourceConfig.setPassword(zkProperties.get(dbId + ".jdbc.password"));
    dataSourceConfig.setDriverClassName(zkProperties.get(dbId + ".jdbc.driverClassName"));

    String initialSize = zkProperties.get(dbId + ".jdbc.initialSize");
    if (StringUtil.isNotBlank(initialSize)) {
      try {
        dataSourceConfig.setInitialSize(Integer.valueOf(initialSize));
      } catch (Exception e) {
        logger.warn("parse initialSize error:{}", initialSize);
      }
    }

    String maxActive = zkProperties.get(dbId + ".jdbc.maxActive");
    if (StringUtil.isNotBlank(maxActive)) {
      try {
        dataSourceConfig.setMaxActive(Integer.valueOf(maxActive));
      } catch (Exception e) {
        logger.warn("parse maxActive error:{}", maxActive);
      }
    }

    String minIdle = zkProperties.get(dbId + ".jdbc.minIdle");
    if (StringUtil.isNotBlank(minIdle)) {
      try {
        dataSourceConfig.setMinIdle(Integer.valueOf(minIdle));
      } catch (Exception e) {
        logger.warn("parse minIdle error:{}", minIdle);
      }
    }

    return dataSourceConfig;
  }

  @Deprecated
  public static DataSourceConfig getDataSourceConfigByDbId(String type, String dbId) {
	  if(!StringUtil.isEmpty(type)) {
		 dbId = type + "." + dbId;
	  }
	  return getDataSourceConfigByDbId(dbId);
  }

  public static ArrayDataSourceConfig getArrayDataSourceConfigByDbId(String type, String dbId) {
    String typeDbId = dbId;
    if(!StringUtil.isEmpty(type)) {
      typeDbId = type + "." + dbId;
    }

    ArrayDataSourceConfig arrayDataSourceConfig = getArrayDataSourceConfigByDbId(typeDbId);
    if (arrayDataSourceConfig == null) {
      return getArrayDataSourceConfigByDbId(dbId);
    }
    return arrayDataSourceConfig;
  }


  public static ArrayDataSourceConfig getArrayDataSourceConfigByDbId(String dbId) {
    String nodelist = zkProperties.get(dbId + ".jdbc.nodelist");
    ArrayDataSourceConfig arrayDataSourceConfig = new ArrayDataSourceConfig(nodelist);
    DataSourceConfig dataSourceConfig = getDataSourceConfigByDbIdAndNodeId(dbId, null);

    if (dbId.equals(CLICKHOUSE_DB_ID)) {
      arrayDataSourceConfig.setDynamicName(CLICKHOUSE_DB_ID);
    }else if (dbId.equals(DORIS_DB_ID)) {
      arrayDataSourceConfig.setDynamicName(DORIS_DB_ID);
    }else if (dbId.equals(DORIS_DW_DB_ID)) {
      arrayDataSourceConfig.setDynamicName(DORIS_DW_DB_ID);
    }else if (dbId.equals(DORIS_HYBIRD_DB_ID)) {
      arrayDataSourceConfig.setDynamicName(DORIS_HYBIRD_DB_ID);
    }else {
      arrayDataSourceConfig.setDynamicName("mysql");
    }
    if (StringUtil.isEmpty(dataSourceConfig.getUrl())) {
      return null;
    }
    arrayDataSourceConfig.add(dataSourceConfig);
    String[] nodeIds = arrayDataSourceConfig.getNodeIds();
    if (nodeIds != null) {
      for (String nodeId : nodeIds) {
        DataSourceConfig nodeDataSourceConfig = getDataSourceConfigByDbIdAndNodeId(dbId, nodeId);
        arrayDataSourceConfig.add(nodeDataSourceConfig);
      }
    }
    logger.info("arrayDataSourceConfig {}", arrayDataSourceConfig);
    return arrayDataSourceConfig;
  }

  public static DataSourceConfig getDataSourceConfigByDbIdAndNodeId(String dbId, String nodeId) {
    DataSourceConfig dataSourceConfig = new DataSourceConfig();
    dataSourceConfig.setUrl(zkProperties.getOrDefault(StringUtil.contact(".", dbId, "jdbc.url", nodeId), zkProperties.get(dbId + ".jdbc.url")));
    dataSourceConfig.setUsername(zkProperties.getOrDefault(StringUtil.contact(".", dbId, "jdbc.username", nodeId), zkProperties.get(dbId + ".jdbc.username")));
    dataSourceConfig.setPassword(zkProperties.getOrDefault(StringUtil.contact(".", dbId, "jdbc.password", nodeId), zkProperties.get(dbId + ".jdbc.password")));
    dataSourceConfig.setDriverClassName(zkProperties.getOrDefault(StringUtil.contact(".", dbId, "jdbc.driverClassName", nodeId), zkProperties.get(dbId + ".jdbc.driverClassName")));
    dataSourceConfig.setDbId(dbId);
    String initialSize = zkProperties.getOrDefault(StringUtil.contact(".", dbId, "jdbc.initialSize", nodeId), zkProperties.get(dbId + ".jdbc.initialSize"));
    if (StringUtil.isNotBlank(initialSize)) {
      try {
        dataSourceConfig.setInitialSize(Integer.valueOf(initialSize));
      } catch (Exception e) {
        logger.warn("parse initialSize error:{}", initialSize);
      }
    }

    String maxActive = zkProperties.getOrDefault(StringUtil.contact(".", dbId, "jdbc.maxActive", nodeId), zkProperties.get(dbId + ".jdbc.maxActive"));
    if (StringUtil.isNotBlank(maxActive)) {
      try {
        dataSourceConfig.setMaxActive(Integer.valueOf(maxActive));
      } catch (Exception e) {
        logger.warn("parse maxActive error:{}", maxActive);
      }
    }

    String minIdle = zkProperties.getOrDefault(StringUtil.contact(".", dbId, "jdbc.minIdle", nodeId), zkProperties.get(dbId + ".jdbc.minIdle"));
    if (StringUtil.isNotBlank(minIdle)) {
      try {
        dataSourceConfig.setMinIdle(Integer.valueOf(minIdle));
      } catch (Exception e) {
        logger.warn("parse minIdle error:{}", minIdle);
      }
    }
    return dataSourceConfig;
  }

  public static List<String> getAllDbId() {
    Set<String> dbIdSet = new TreeSet<>();

    String dbIdListStr = zkProperties.get(DB_ID_LIST);
    strToList(dbIdSet, dbIdListStr);
    for (APPEnum app : APPEnum.values()) {
      String appDbIdListStr = zkProperties.get(String.format(APP_DB_ID_LIST, app.getId()));
      strToList(dbIdSet, appDbIdListStr);
    }

    return new ArrayList<>(dbIdSet);
  }

  public static List<String> getAllDbId(Long appId) {
      Set<String> dbIdSet = new TreeSet<>();
      String appDbIdListStr = zkProperties.get(String.format(APP_DB_ID_LIST, appId));
      strToList(dbIdSet, appDbIdListStr);
      if (dbIdSet.isEmpty()) {
          String currentAppDbId = String.format(CURRENT_APP_DB_ID, appId);
          String curDbId = zkProperties.get(currentAppDbId);
          if (StringUtil.isNotEmpty(curDbId)) {
              dbIdSet.add(curDbId);
          }
      }
      if (dbIdSet.isEmpty()) {
          return getAllDbId();
      }
      return new ArrayList<>(dbIdSet);
  }

  @Deprecated
  public static String getCurrentDbId() {
    String curDbId = zkProperties.get(CURRENT_DB_ID);
    return curDbId;
  }

  public static String getCurrentDbId(Long appId) {
    String currentAppDbId = String.format(CURRENT_APP_DB_ID, appId);
    String curDbId = zkProperties.get(currentAppDbId);
    if (StringUtil.isEmpty(curDbId)) {
      return getCurrentDbId();
    }
    return curDbId;
  }

  private static void strToList(Set<String> dbIdSet, String dbIdListStr) {
    if (StringUtil.isNotEmpty(dbIdListStr)) {
      String[] strs = dbIdListStr.split(",");
      for (String str : strs) {
        String trimedStr = str.trim();
        if (!"".equals(trimedStr)) {
          dbIdSet.add(trimedStr);
        }
      }
    }
  }


}


